/*
	Written by: Daniel Mathis
	
	Contains the main game loop, sdl setup routines and object declarations.
*/

/*
 * This code was created by Jeff Molofee '99 
 * (ported to Linux/SDL by Ti Leggett '01)
 *
 * If you've found this code useful, please let me know.
 *
 * Visit Jeff at http://nehe.gamedev.net/
 * 
 * or for port-specific comments, questions, bugreports etc. 
 * email to leggett@eecs.tulane.edu
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "SDL.h"
#include <time.h>
#include <vector>
#include "GameObjects.h"
#include "GameLogic.h"

/* screen width, height, and bit depth */
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP     16

/* Define our booleans */
#define TRUE  1
#define FALSE 0

/* This is our SDL surface */
SDL_Surface *surface;

/* function to release/destroy our resources and restoring the old desktop */
void Quit( int returnCode )
{
    /* clean up the window */
    SDL_Quit( );

    /* and exit appropriately */
    exit( returnCode );
}

int initSDL()
{
    /* Flags to pass to SDL_SetVideoMode */
    int videoFlags;


    /* this holds some info about our display */
    const SDL_VideoInfo *videoInfo;


    /* initialize SDL */
    if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
	{
	    fprintf( stderr, "Video initialization failed: %s\n",
		     SDL_GetError( ) );
	    Quit( 1 );
	}

    /* Fetch the video info */
     videoInfo = SDL_GetVideoInfo( );

     if ( !videoInfo )
	{
	    fprintf( stderr, "Video query failed: %s\n",
		     SDL_GetError( ) );
	    Quit( 1 );
	}

    /* the flags to pass to SDL_SetVideoMode */
    videoFlags  = SDL_OPENGL;          /* Enable OpenGL in SDL */
    videoFlags |= SDL_GL_DOUBLEBUFFER; /* Enable double buffering */
    videoFlags |= SDL_HWPALETTE;       /* Store the palette in hardware */
    videoFlags |= SDL_RESIZABLE;       /* Enable window resizing */

    /* This checks to see if surfaces can be stored in memory */
    if ( videoInfo->hw_available )
	videoFlags |= SDL_HWSURFACE;
    else
	videoFlags |= SDL_SWSURFACE;

    /* This checks if hardware blits can be done */
    if ( videoInfo->blit_hw )
	videoFlags |= SDL_HWACCEL;

    /* Sets up OpenGL double buffering */
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    /* get a SDL surface */
    surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
				videoFlags );

    /* Verify there is a surface */
    if ( !surface )
	{
	    fprintf( stderr,  "Video mode set failed: %s\n", SDL_GetError( ) );
	    Quit( 1 );
	}
	return videoFlags;
}

/* function to reset our viewport after a window resize */
int resizeWindow( int width, int height )
{
    /* Protect against a divide by zero */
    if ( height == 0 )
	height = 1;

    /* Setup our viewport. */
    glViewport( 0, 0, ( GLsizei )width, ( GLsizei )height );

    /* change to the projection matrix and set our viewing volume. */
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    /* Set our perspective */
    gluOrtho2D( 0, SCREEN_WIDTH, 0, SCREEN_HEIGHT);

    /* Make sure we're chaning the model view and not the projection */
    glMatrixMode( GL_MODELVIEW );

    /* Reset The View */
    glLoadIdentity( );

    return( TRUE );
}

/* function to handle key press events */
void handleKeyPress( SDL_keysym *keysym, ShipController &sc, BulletController &bc)
{
    switch ( keysym->sym )
	{
	case SDLK_ESCAPE:
	    /* ESC key was pressed */
	    Quit( 0 );
	    break;
	case SDLK_F1:
	    /* F1 key was pressed
	     * this toggles fullscreen mode
	     */
	    SDL_WM_ToggleFullScreen( surface );
	    break;
	case SDLK_UP:
		sc.getShip()->accelerate(true);
		break;
	case SDLK_DOWN:
		sc.getShip()->decelerate(true);
		break;
	case SDLK_RIGHT:
		sc.getShip()->turnRight(true);
		break;
	case SDLK_LEFT:
		sc.getShip()->turnLeft(true);
		break;
	case SDLK_SPACE:
		bc.generateBullet(sc.getShip()->itsXLoc(), sc.getShip()->itsYLoc(), sc.getShip()->itsDirection());
		break;
	default:
	    break;
	}

    return;
}

/* function to handle key release events */
void handleKeyRelease( SDL_keysym *keysym, ShipController &sc, BulletController &bc)
{
 switch ( keysym->sym )
	{
		case SDLK_UP:
			sc.getShip()->accelerate(false);
			break;
		case SDLK_DOWN:
			sc.getShip()->decelerate(false);
			break;
		case SDLK_RIGHT:
			sc.getShip()->turnRight(false);
			break;
		case SDLK_LEFT:
			sc.getShip()->turnLeft(false);
			break;
		default:
	    	break;
	}
	
	return;
}

/* general OpenGL initialization function */
int initGL( )
{

    /* Enable smooth shading */
    glShadeModel( GL_SMOOTH );

    /* Set the background black */
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

    /* Depth buffer setup */
    glClearDepth( 1.0f );

    /* Enables Depth Testing */
    glEnable( GL_DEPTH_TEST );

    /* The Type Of Depth Test To Do */
    glDepthFunc( GL_LEQUAL );

    return( TRUE );
}

/* Here goes our drawing code */
int drawGLScene(ShipController &sc, AsteroidController &ac, BulletController &bc)
{
    /* Clear The Screen And The Depth Buffer */
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	/* Draw each object in sb, ac and bc */
	sc.draw();
	ac.draw();
	bc.draw();
    	
    /* Draw it to the screen */
    SDL_GL_SwapBuffers( );
    return( TRUE );
}

int main( int argc, char **argv )
{	int videoFlags;

	/* initialize SDL */
	videoFlags = initSDL();
	
    /* initialize OpenGL */
    initGL( );

    /* resize the initial window */
    resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT );
    /* main loop variable */
    int done = FALSE;
    /* whether or not the window is active */
    int isActive = TRUE;
    /* used to collect events */
    SDL_Event event;
    /* initialize random seed */
    srand(time(NULL));
    
	// Create object controllers
	ShipController sc;
	AsteroidController ac;
	BulletController bc;
	
	// Create ship
	sc.generateShip(20, 20, 100, 100);
	// Create asteroids
	ac.generateAsteroids(15, 30, 30);
	
	// Generate game logic controller
	LogicController lc(sc, ac, bc);
    /* setup variables used for timing */
    clock_t time_now, movement_amt = 0;
    double tick = CLOCKS_PER_SEC/30;
    /* wait for events */ 
    while ( !done )
	{
	    /* handle the events in the queue */

	    while ( SDL_PollEvent( &event ) )
		{
		    switch( event.type )
			{
			case SDL_ACTIVEEVENT:
			    /* Something's happend with our focus
			     * If we lost focus or we are iconified, we
			     * shouldn't draw the screen
			     */
			    if ( event.active.gain == 0 )
				isActive = FALSE;
			    else
				isActive = TRUE;
			    break;			    
			case SDL_VIDEORESIZE:
			    /* handle resize event */
			    surface = SDL_SetVideoMode( event.resize.w,
							event.resize.h,
							16, videoFlags );
			    if ( !surface )
				{
				    fprintf( stderr, "Could not get a surface after resize: %s\n", SDL_GetError( ) );
				    Quit( 1 );
				}
			    resizeWindow( event.resize.w, event.resize.h );
			    break;
			case SDL_KEYDOWN:
			    /* handle key presses */
			    handleKeyPress( &event.key.keysym, sc, bc);
			    break;
			case SDL_KEYUP:
			    /* handle key presses */
			    handleKeyRelease( &event.key.keysym, sc, bc);
			    break;			 
			case SDL_QUIT:
			    /* handle quit requests */
			    done = TRUE;
			    break;
			default:
			    break;
			}
		}
		// Retrieve timing information, and execute logic if within bounds
		time_now = clock();
		if(time_now - movement_amt > tick)
		{
			// Handle movement for each object this click
			sc.move();
			ac.move();
			bc.move();
			
			// Handle collision detection
			if(lc.isCollision())
			{
				// Do something
			}
		}
	    /* draw the scene */
	    if ( isActive )
		drawGLScene(sc, ac, bc);
	}

    /* clean ourselves up and exit */
    Quit( 0 );

    /* Should never get here */
    return( 0 );
}
